﻿using System;
using System.IO;
using System.Data;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing.Drawing2D;     // ZOMG namespace staircase!
using System.Collections.Generic;
using System.Windows.Forms.DataVisualization.Charting;

namespace FriendlyFire.UserControls
{
    //TODO: Add image category support. jpg,png,gif,jpeg,bmp,tif,raw,
    public partial class Indexer : UserControl
    {
        // vars
        private Helper myHelper;

        #region Constructor
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="ffHelper">Reference to friendly fire helper</param>
        public Indexer(ref Helper ffHelper)
        {
            InitializeComponent();

            myHelper = ffHelper;

            indexWorker.WorkerReportsProgress = true;
            componentLoader.RunWorkerAsync();           // calculates the current number of files indexed
            updatePieChart.RunWorkerAsync();    // Pie Charts are fun


            indexPanel.Visible = false;
            browsePanel.Location = new Point(0, 0);
            browsePanel.Visible = true;

            dirsToIndex.DataSource = myHelper.DBHelper.GetIndexDirs().ToList();
            dirsToIndex.DisplayMember = "Value";
            dirsToIndex.ValueMember = "Key";
        }
        #endregion
        #region addDirToList
        /// <summary>
        /// Takes the chosen directory and adds
        /// the location to the database. The index is 
        /// not rebuilt at this point, simply the list
        /// of directories.
        /// </summary>
        private void addDirToList_Click(object sender, EventArgs e)
        {
            DialogResult dr = dirChooser.ShowDialog();
            if (dr == DialogResult.OK)
            {
                // A directory was chosen.
                // add it tot he list.
                if (myHelper.DBHelper.CheckDirectoryCount(dirChooser.SelectedPath) == 0)
                {
                    myHelper.DBHelper.InsertNewIndexedDirectory(dirChooser.SelectedPath);
                    dirsToIndex.DataSource = myHelper.DBHelper.GetIndexDirs().ToList();
                    dirsToIndex.DisplayMember = "Value";
                    dirsToIndex.ValueMember = "Key";
                }
            }
        }
        #endregion
        #region removeDirFromList
        /// <summary>
        /// Removes the selected directory from
        /// the index list. The index is not updated
        /// at this point.
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void removeDirFromList_Click(object sender, EventArgs e)
        {
            if (dirsToIndex.SelectedIndex >= 0) // if the index = -1, there is no selected item
            {
                myHelper.DBHelper.DeleteIndexedDirectory((KeyValuePair<long, string>)dirsToIndex.SelectedItem);
                dirsToIndex.DataSource = myHelper.DBHelper.GetIndexDirs().ToList();
                dirsToIndex.DisplayMember = "Value";
                dirsToIndex.ValueMember = "Key";
            }
        }
        #endregion
        #region buildIndex
        /// <summary>
        /// Takes each directory in the list
        /// and passes it to a background worker.
        /// This can take a while depending on the
        /// number of files and type of media.
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void buildIndex_Click(object sender, EventArgs e)
        {
            prog.Value = 0;
            progPercent.Text = "0"; // reset progress bar
            List<DirectoryInfo> dirs = new List<DirectoryInfo>();
            // Create and pass StateInfo obj to the bgrnd worker
            foreach (KeyValuePair<long,string> k in dirsToIndex.Items)
            {
                dirs.Add(new DirectoryInfo(k.Value));
            }
            IndexStateInfo si = new IndexStateInfo();
            si.Dirs = dirs;
            indexWorker.RunWorkerAsync(si); // build file data. Then call updatePieChart when done.
        }
        #endregion
        #region resetIndex
        /// <summary>
        /// Resets the user's index. Deletes all
        /// file entries in addtion to any linked
        /// metadata. This method also updates the file
        /// count and the dirsToIndex list in the GUI.
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void resetIndex_Click(object sender, EventArgs e)
        {
            if (myHelper.DBHelper.ResetIndex())
            {
                numIndexed.Text = "0";
                dirsToIndex.DataSource = myHelper.DBHelper.GetIndexDirs().ToList();
                dirsToIndex.DisplayMember = "Value";
                dirsToIndex.ValueMember = "Key";
            }
        }
        #endregion
        #region listReset
        /// <summary>
        /// Deletes all of a user's 'lists'
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void listReset_Click(object sender, EventArgs e)
        {
            myHelper.DBHelper.ResetLists();
        }
        #endregion
        #region indexWorker
        /// <summary>
        /// This method is the powerhorse of this control.
        /// It systematically works its way through the
        /// directories provided by the user and works its
        /// way through every file within them. At this time
        /// it is also parsing out any and all metadata we are
        /// able to extract from certain filetypes.
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void indexWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            // reset the index. this also invalidates lists.
            myHelper.DBHelper.ResetIndex();

            List<string> allFiles = new List<string>();
            foreach (DirectoryInfo d in ((IndexStateInfo)e.Argument).Dirs)
            {
                if (d.Exists)
                {
                    foreach (string s in Directory.GetFiles(d.FullName, "*.*", SearchOption.AllDirectories))
                    {
                        allFiles.Add(s);
                    }
                }
            }

            double fileCount = allFiles.Count;
            double percent = 100f / fileCount;
            double sum = 0f;

            FileInfo f;
            IndexStateInfo si = new IndexStateInfo();
            TagLib.File tl;
            MetaDataInfo md = new MetaDataInfo();
            FileDataInfo file = new FileDataInfo();
            foreach (string s in allFiles)
            {
                f = new FileInfo(s);
                if (f.Extension.ToLower() == ".mp3")
                {
                    try
                    {
                        tl = TagLib.File.Create(f.FullName);
                        md.Album = tl.Tag.Album;
                        if (tl.Tag.Performers.Length > 0)
                            md.Artist = tl.Tag.Performers[0];
                        md.Bitrate = tl.Properties.AudioBitrate;
                        if (tl.Tag.Composers.Length > 0)
                            md.Composer = tl.Tag.Composers[0];
                        md.Duration = tl.Properties.Duration.ToString();
                        if (tl.Tag.Genres.Length > 0)
                            md.Genre = tl.Tag.Genres[0];
                        md.Title = tl.Tag.Title;
                        md.Track = tl.Tag.Track;
                        md.Year = tl.Tag.Year;
                    }
                    catch //(CorruptFileException corr)
                    {
                        goto treatAsNormalFile;
                    }
                }
                else if (f.Extension.ToLower() == ".avi")
                {
                    try
                    {
                        tl = TagLib.File.Create(f.FullName);
                        md.Height = tl.Properties.VideoHeight;
                        md.Width = tl.Properties.VideoWidth;
                        md.Duration = tl.Properties.Duration.ToString();
                    }
                    catch
                    {
                        goto treatAsNormalFile;
                    }
                }
            treatAsNormalFile:
                md.ByteSize = f.Length;

                myHelper.DBHelper.InsertNewMetaDataRecord(md);
                si.Percent = percent;
                sum += percent;
                indexWorker.ReportProgress(Convert.ToInt32(sum), si);

                file.Name = f.Name;
                file.MetadataID = (long)myHelper.DBHelper.GetLastInsertID();    //SELECT Last_Insert_Rowid()
                file.Path = f.DirectoryName;
                file.Extension = f.Extension.ToLower();
                myHelper.DBHelper.InsertNewFileRecord(file);
                // Testing only.// System.Threading.Thread.Sleep(100);

                f = null;
                tl = null;
             
            }
        }
        #endregion

        #region Extra background work stuff
        #region indexWorker_RunWorkerCompleted
        /// <summary>
        /// Displays number files index once index is completed
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void indexWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            numIndexed.Text = myHelper.DBHelper.CountFilesIndexed().ToString();
            progPercent.Text = "100.000000000000";
            updatePieChart.RunWorkerAsync();    // Pie Charts are fun
        }
        #endregion
        #region indexWorker_ProgressChanged
        /// <summary>
        /// Event that fires whenever the index worker has made progress, updates the progress bar
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void indexWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progPercent.Text = (Convert.ToDouble(progPercent.Text) + ((IndexStateInfo)e.UserState).Percent).ToString();
            prog.Value = e.ProgressPercentage;
        }
        #endregion
        #region componentLoader_DoWork
        /// <summary>
        /// This event updates the total number of files indexed while the program is loading
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void componentLoader_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = myHelper.DBHelper.CountFilesIndexed();
        }
        #endregion
        #region componentLoader_RunWorkerCompleted
        /// <summary>
        /// Displays number of files indexed on program load
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void componentLoader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            numIndexed.Text = e.Result.ToString();
        }
        #endregion
        #endregion
        #region Indexer_Paint
        /// <summary>
        /// Overrides the paint function to draw gradients and enable double buffering to prevent flickering on resize
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void Indexer_Paint(object sender, PaintEventArgs e)
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            myHelper.DrawDefaultBackGroundGradient(e.Graphics, ClientRectangle);
        }
        #endregion
        #region PieChart Workers
        #region updatePieChart_RunWorkerCompleted
        /// <summary>
        /// draw the pie chart
        /// </summary>
        private void updatePieChart_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // Set Doughnut chart type
            chart1.Series[0].ChartType = SeriesChartType.Pie;

            // Enable 3D
            chart1.ChartAreas[0].Area3DStyle.Enable3D = true;

            // Set drawing style
            chart1.Series[0]["PieDrawingStyle"] = "SoftEdge";

            // Populate series data
            chart1.Series[0].Points.DataBindXY(((PieChartData)e.Result).XValues, ((PieChartData)e.Result).YValues);
        }
        #endregion
        #region updatePieChart_DoWork
        private void updatePieChart_DoWork(object sender, DoWorkEventArgs e)
        {
            PieChartData results = new PieChartData();
            long arch = 0;
            foreach (string s in myHelper.DBHelper.ExtArch)
            {
                arch += myHelper.DBHelper.CountFilesWithGivenExtension(s);
            }
            long aud = 0;
            foreach (string s in myHelper.DBHelper.ExtAud)
            {
                aud += myHelper.DBHelper.CountFilesWithGivenExtension(s);
            }
            long doc = 0;
            foreach (string s in myHelper.DBHelper.ExtDocs)
            {
                doc += myHelper.DBHelper.CountFilesWithGivenExtension(s);
            }
            long vid = 0;
            foreach (string s in myHelper.DBHelper.ExtVid)
            {
                vid += myHelper.DBHelper.CountFilesWithGivenExtension(s);
            }
            List<double> yvals = new List<double>();
            List<string> xvals = new List<string>();
            if (arch > 0)
            {
                yvals.Add(Convert.ToDouble(arch));
                xvals.Add("Archive");
            }
            if (aud > 0)
            {
                yvals.Add(Convert.ToDouble(aud));
                xvals.Add("Audio");
            }
            if (doc > 0)
            {
                yvals.Add(Convert.ToDouble(doc));

                xvals.Add("");
            }
            if (vid > 0)
            {
                yvals.Add(Convert.ToDouble(vid));
                xvals.Add("Video");
            }

            results.YValues = yvals.ToArray();
            results.XValues = xvals.ToArray();

            e.Result = results;
        }
        #endregion

        #endregion

        private void btnSetupIndexFolders_Click(object sender, EventArgs e)
        {
            indexPanel.Visible = true;
            browsePanel.Visible = false;
        }
        private void btnBrowseLibrary_Click(object sender, EventArgs e)
        {
            indexPanel.Visible = false;
            browsePanel.Location = new Point(0, 0);
            browsePanel.Visible = true;
        }
    }

    #region PieChartData Struct
    /// <summary>
    /// Small struct used to pass
    /// pie chart data from the background worker
    /// to the render control.
    /// </summary>
    public struct PieChartData
    {
        double[] yValues;
        string[] xValues;

        public double[] YValues
        {
            get { return yValues; }
            set { yValues = value; }
        }
        public string[] XValues
        {
            get { return xValues; }
            set { xValues = value; }
        }
    }
    #endregion
}
